package com.example.rjb_shoping.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.rjb_shoping.config.RjbConstant;
import com.example.rjb_shoping.dao.RjbCommodityDao;
import com.example.rjb_shoping.dao.RjbOrderDao;
import com.example.rjb_shoping.dao.RjbSpkieDao;
import com.example.rjb_shoping.entity.RjbCommodity;
import com.example.rjb_shoping.entity.RjbOrder;
import com.example.rjb_shoping.entity.RjbRule;
import com.example.rjb_shoping.query.OrderQuery;
import com.example.rjb_shoping.query.SeckillRequest;
import com.example.rjb_shoping.query.UserQuery;
import com.example.rjb_shoping.service.RjbCommodityService;
import com.example.rjb_shoping.service.RjbOrderService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import com.example.rjb_shoping.util.RedisUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.xq.softcup.mq.client.consumer.IMqConsumer;
import org.xq.softcup.mq.client.consumer.annotation.MqConsumer;
import org.xq.softcup.mq.client.util.MqResult;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * (RjbOrder)表服务实现类
 *
 * @author makejava
 * @since 2021-06-09 20:43:02
 */
@Slf4j
@MqConsumer(topic = "topic_order")
@Service("rjbOrderService")
public class RjbOrderServiceImpl implements RjbOrderService, IMqConsumer {
    @Resource
    private RjbOrderDao rjbOrderDao;

    @Resource
    private RjbSpkieDao rjbSpkieDao;

    @Resource
    private RjbCommodityDao rjbCommodityDao;

    @Resource
    private RjbCommodityService rjbCommodityService;

    @Resource
    private RedisUtils redisUtils;


    @Resource
    TransactionTemplate transactionTemplate;

    final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("Orders-%d")
            .setDaemon(true)
            .build();
    //订单处理多线程
    private ExecutorService executorService = Executors.newFixedThreadPool(20);
    //    private AtomicBoolean executeFlag = new AtomicBoolean(false);
    private volatile boolean executorStoped = false;
    //订单队列
    private volatile LinkedBlockingQueue<RjbOrder> orderQueue = new LinkedBlockingQueue<RjbOrder>();

    private List<RjbRule> rjbRules = null;

    /**
     * 初始化插入订单多线程
     */
    @PostConstruct
    public void initMethod() {
        rjbRules = (List<RjbRule>) redisUtils.get(RjbConstant.SECKILL_DISCOUNT);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (!executorStoped) {
                    RjbOrder order = null;
                    try {
                        //从订单队列中获取订单对象
                        order = orderQueue.take();
                        if (order != null) {

                            //数据库添加订单操作
                            insertSpikeOrder(order);
                        }
                    } catch (Exception e) {
                        if (!executorStoped) {
                            log.error(e.getMessage(), e);
                            if (order != null) {
                                synchronized (order) {
                                    //Redis中缓存订单下单失败状态(-1), 用于在前端查看订单结果使用,缓存限时1个小时
                                    redisUtils.set(RjbConstant.ORDER_FOR_SECKILL + order.getSId() + ":" + order.getMId() + ":" + order.getCId(), -1, 3600, TimeUnit.SECONDS);
                                }
                            }
                        }
                    }
                }
            }
        });

    }

    @PreDestroy
    public void preDestory() {
        executorStoped = true;
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    @Override
    public RjbOrder queryById(Integer oId) {
        return this.rjbOrderDao.queryById(oId);
    }

    @Override
    public List<RjbOrder> queryAllByLimit(int offset, int limit) {
        return this.rjbOrderDao.queryAllByLimit(offset, limit);
    }

    @Override
    public PageInfo<RjbOrder> limitList(OrderQuery orderQuery) {
        PageHelper.startPage(orderQuery.getPageNum(), orderQuery.getPageSize());
        List<RjbOrder> rjbOrders = rjbOrderDao.queryByList(orderQuery.getSId());
        PageInfo pageInfo = new PageInfo(rjbOrders);
        return pageInfo;
    }

    /**
     * 新增数据
     *
     * @param rjbOrder 实例对象
     * @return 实例对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public RjbOrder insert(RjbOrder rjbOrder) throws Exception {
        this.rjbOrderDao.insert(rjbOrder);
        return rjbOrder;
    }

    /**
     * 新增数据
     *
     * @param rjbOrder 实例对象
     * @return 实例对象
     */
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public RjbOrder insertSpikeOrder(RjbOrder rjbOrder) throws Exception {
        /**
         *启动多线程
         */
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                //JDBC 编程式事务
                transactionTemplate.execute((status)->{
                    //保存订单
                    try {
                        //redis减秒杀商品预加载的库存
                        Object sId = redisUtils.lrPop(RjbConstant.TODAY_SECKILL_STOCK + rjbOrder.getSId() + ":" + rjbOrder.getMId());
                        if (sId == null) {
                            log.error("商品{} 已售罄!", rjbOrder.getMId());
                            //Redis中缓存订单下单失败状态(-1), 用于在前端查看订单结果使用,缓存限时1个小时
                            redisUtils.set(RjbConstant.ORDER_FOR_SECKILL + rjbOrder.getSId() + ":" + rjbOrder.getMId() + ":" + rjbOrder.getCId(), -1, 3600, TimeUnit.SECONDS);
                            throw new Exception("商品已售罄！");
                        }

                        //减redis秒杀商品库存 rjbCommodityDao.reduceStock(order.getMId(), order.getOCount());
                        int row = rjbSpkieDao.reduceStock(rjbOrder.getSId(), rjbOrder.getOCount());
                        if (row == 0) {
                            log.error("商品{} 库存不足!", rjbOrder.getMId());
                            //Redis中缓存订单下单失败状态(-1), 用于在前端查看订单结果使用,缓存限时1个小时
                            redisUtils.set(RjbConstant.ORDER_FOR_SECKILL + rjbOrder.getSId() + ":" + rjbOrder.getMId() + ":" + rjbOrder.getCId(), -1, 3600, TimeUnit.SECONDS);
                            //删除秒杀订单计数器
                            redisUtils.del(RjbConstant.ORDER_FOR_SECKILL_COUNTER + ":" + rjbOrder.getSId());
                            throw new Exception("库存不足！");
                        }
                        List<RjbRule> rjbRules = RjbOrderServiceImpl.this.rjbRules;
                        if (rjbRules == null) {
                            RjbOrderServiceImpl.this.rjbRules = (List<RjbRule>) redisUtils.get(RjbConstant.SECKILL_DISCOUNT);
                            rjbRules = RjbOrderServiceImpl.this.rjbRules;
                        }
                        //秒杀订单计数器，用于排序号
                        long increment = redisUtils.increment(RjbConstant.ORDER_FOR_SECKILL_COUNTER + ":" + rjbOrder.getSId());
                        rjbOrder.setOSort((int) increment); //设置订单序号
                        for (RjbRule rjbRule : rjbRules) {
                            if (rjbRule.getRNumber() >= increment) {
                                //获取打折数
                                int discount = Integer.parseInt(rjbRule.getRRule());
                                //计算打折后价格
                                double actualPay = rjbOrder.getOOrginal() * (discount / 10.0);
                                rjbOrder.setOActual(actualPay);
                                break;
                            }
                        }
                        rjbOrderDao.insert(rjbOrder);

                        //Redis中缓存订单下单成功状态(订单ID), 用于在前端查看订单结果使用,缓存限时1个小时
                        redisUtils.set(RjbConstant.ORDER_FOR_SECKILL + rjbOrder.getSId() + ":" + rjbOrder.getMId() + ":" + rjbOrder.getCId(), rjbOrder.getOId(), 3600, TimeUnit.SECONDS);
                        log.debug("插入{}", rjbOrder.getCId());
                        return Boolean.TRUE;
                    } catch (DuplicateKeyException e) {
                        log.error("重复订单:{}", e.getMessage());
                        //标记回滚操作
                        status.setRollbackOnly();
                        //恢复redis减掉的库存
                        redisUtils.llSet(RjbConstant.TODAY_SECKILL_STOCK+rjbOrder.getSId()+":"+ rjbOrder.getMId(), rjbOrder.getSId(), 60*60*24-60);
                        return Boolean.FALSE;
                    } catch (Exception e) {
                        log.error("创建订单失败:{}", e);
                        //恢复redis减掉的库存
                        redisUtils.llSet(RjbConstant.TODAY_SECKILL_STOCK+rjbOrder.getSId()+":"+ rjbOrder.getMId(), rjbOrder.getSId(), 60*60*24-60);
                        //标记回滚操作
                        status.setRollbackOnly();
                        //Redis中缓存订单下单失败状态(订单ID), 用于在前端查看订单结果使用,缓存限时1个小时
                        redisUtils.set(RjbConstant.ORDER_FOR_SECKILL + rjbOrder.getSId() + ":" + rjbOrder.getMId() + ":" + rjbOrder.getCId(), -1, 3600, TimeUnit.SECONDS);
                        return Boolean.FALSE;
                    }

                }); //end of 事务
            }
        }, rjbOrder);
        return rjbOrder;
    }

    /**
     * 普通下单
     *
     * @param rjbOrder 实例对象
     * @return 实例对象
     */
    @Override
    public RjbOrder update(RjbOrder rjbOrder) {
        log.info("用户订单对象" + rjbOrder);
        int mid = rjbOrder.getMId();
        RjbCommodity rjbCommodity = rjbCommodityService.queryById(mid);
        if (rjbCommodity != null) {
            int i = this.rjbOrderDao.insert(rjbOrder);
            if (i > 0) {
                String oNumber = rjbOrder.getONumber();
                String[] list = oNumber.split("0");
                int number = Integer.parseInt(list[list.length - 1]);
                // System.out.println("下单数量 -->" + number);
                rjbCommodity.setMInstock(rjbCommodity.getMInstock() - number);
                rjbCommodity.setMSales(rjbCommodity.getMSales() + number);
                rjbCommodityService.update(rjbCommodity);
                return this.queryById(rjbOrder.getOId());
            }
        }
        return null;
    }

    @Override
    public boolean deleteById(Integer oId) {
        return this.rjbOrderDao.deleteById(oId) > 0;
    }

    @Override
    public List<RjbOrder> queryByList(int sId) {
        List<RjbOrder> list = new ArrayList<>();
        List<RjbOrder> rjbList = rjbOrderDao.queryByList(sId);
        if (rjbList != null) list = rjbList;
        return list;
    }

    @Override
    public List<RjbOrder> selectByCidList(Integer cid) {
        return rjbOrderDao.selectByCidList(cid);
    }

    @Override
    public List<RjbOrder> likeAll(long number) {
        return rjbOrderDao.likeAll(number);
    }

    @Override
    public int orderResult(SeckillRequest request) {
        if (request != null) {
            int orderId = rjbOrderDao.orderResult(request);
            if (orderId > 0) {
                return orderId; //下单成功
            } else {
                Object oId = redisUtils.hget(RjbConstant.ORDER_FOR_SECKILL + request.getSId() + request.getMId(), request.getCustomerId().toString());
                if (oId == null) {
                    return -1; //下单失败
                } else {
                    return 0; //下单中
                }
            }
        }
        return 0;
    }

    /**
     * 下单（无用）
     *
     * @param rjbOrder
     * @return
     */
    public RjbOrder shoppingCart(RjbOrder rjbOrder) {
        return null;
    }

    /**
     * 消息队列消费者方法
     *
     * @param msgData 消息数据
     * @return
     * @throws Exception
     */
    @Override
    public MqResult consume(String msgData) throws Exception {
        return addMsg(msgData);
    }

    @Override
    public MqResult addMsg(String msgData) throws Exception {
        if (StringUtils.isEmpty(msgData)) {
            log.error("消息为空!");
            return MqResult.FAIL;
        }
        //JSON转订单对象
        RjbOrder order = JSON.parseObject(msgData, RjbOrder.class);
        //添加到订单队列
        orderQueue.put(order);
        log.info("用户:{} 已入订单, 队列size:{}", order.getCId(), orderQueue.size());
        return MqResult.SUCCESS;
    }

    @Transactional(rollbackFor = Exception.class)
    public RjbOrder normalOrder(RjbOrder rjbOrder) throws Exception {
        int mid = rjbOrder.getMId();
        RjbCommodity rjbCommodity = rjbCommodityDao.queryById(mid);
        Integer number = rjbCommodity.getMInstock();
        if (number >= rjbOrder.getOCount()) {
            int i = rjbOrder.getOCount();
            rjbCommodity.setMSales(rjbCommodity.getMStatus() + i);
            rjbCommodity.setMInstock(rjbCommodity.getMInstock() - i);
            int j = rjbCommodityDao.update(rjbCommodity);
            if (j > 0) {
                return this.insert(rjbOrder);
            }
        }else if(rjbCommodity.getMInstock() <= 0){
            rjbCommodity.setMStatus(-1);
            rjbCommodityDao.update(rjbCommodity);
        }
        return null;
    }

}
